using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;

namespace ReportMaintenanceUtility
{
	/// <summary>
	/// An object based-representation of a SQL Server Reporting Services report parameter that encapsulates
	/// all properties and attributes of a parameter that are accessible through the Report Designer UI.
	/// </summary>
	public class Parameter : GenericBase<Parameter>
	{
		#region PRIVATE MEMBERS
		private ReportParameterType			_seed			= null;
		private string						_name			= string.Empty;
		private string						_prompt			= string.Empty;
		private bool						_hidden			= false;
		private bool						_multiValue		= false;
		private bool						_nullable		= false;
		private bool						_allowBlank		= false;
		private bool						_usedInQuery	= false;
		private ReportParameterTypeDataType	_dataType		= ReportParameterTypeDataType.String;
		private ValidValues					_validValues	= null;
		private DefaultValues				_defaultValue	= null;
		private Dictionary<string, DataSet> _dsLibrary		= null;
		#endregion

		#region PUBLIC PROPERTIES

		/// <summary>
		/// Gets or Sets the name of the report parameter, as defined in the RDL
		/// </summary>
		public string Name
		{
			get { return this._name; }
			set
			{
				this.PrepStateChange("Name");
				this._name = value;
			}
		}
		
		/// <summary>
		/// Gets or Sets the prompt label to display for this parameter
		/// </summary>
		public string Prompt
		{
			get { return this._prompt; }
			set
			{
				this.PrepStateChange("Prompt");
				this._prompt = value;
			}
		}
		
		/// <summary>
		/// Gets or Sets whether the current parameter should be defined as Hidden in the RDL
		/// </summary>
		public bool Hidden
		{
			get { return this._hidden; }
			set
			{
				this.PrepStateChange("Hidden");
				this._hidden = value;
			}
		}
		
		/// <summary>
		/// Indicates whether the current parameter is considered internal
		/// </summary>
		public bool Internal
		{
			get { return this._hidden && string.IsNullOrEmpty(this._prompt); }
		}
		
		/// <summary>
		/// Gets or Sets whether the current parameter should be defined as MultiValue in the RDL
		/// </summary>
		public bool MultiValue
		{
			get { return this._multiValue; }
			set
			{
				this.PrepStateChange("MultiValue");
				this._multiValue = value;
			}
		}
		
		/// <summary>
		/// Gets or Sets whether the current parameter should be defined as AllowBlank in the RDL
		/// </summary>
		public bool AllowBlank
		{
			get { return this._allowBlank; }
			set
			{
				this.PrepStateChange("AllowBlank");
				this._allowBlank = value;
			}
		}
		
		/// <summary>
		/// Gets or Sets whether the current parameter should be defined as Nullable in the RDL
		/// </summary>
		public bool Nullable
		{
			get { return this._nullable; }
			set
			{
				this.PrepStateChange("Nullable");
				this._nullable = value;
			}
		}
		
		/// <summary>
		/// Indicates whether the current parameter is used as a value source for a SQL Parameter in a DataSet query
		/// </summary>
		public bool UsedInQuery
		{
			get { return this._usedInQuery; }
		}
		
		/// <summary>
		/// Gets or Sets the defined DataType of the current parameter
		/// </summary>
		public ReportParameterTypeDataType DataType
		{
			get { return this._dataType; }
			set
			{
				this.PrepStateChange("DataType");
				this._dataType = value;
			}
		}
		
		/// <summary>
		/// Gets the object used by this paramter to define how to generate its list of available values
		/// </summary>
		public ValidValues ValidValues
		{
			get { return this._validValues; }
			set
			{
				this.PrepStateChange("ValidValues");
				if (value == null)
				{
					this.DeleteValidValues();
				}
				else
				{
					this.CreateValidValues(value);
				}
			}
		}
		
		/// <summary>
		/// Gets the object used by this paramter to define how to generate its list of default values
		/// </summary>
		public DefaultValues DefaultValue
		{
			get { return this._defaultValue; }
			set
			{
				this.PrepStateChange("DefaultValue");
				if (value == null)
				{
					this.DeleteDefaultValues();
				}
				else
				{
					this.CreateDefaultValues(value);
				}
			}
		}
		
		/// <summary>
		/// A shortcut to the DataSet used by the ValidValues object to generate the parameter's list of available values
		/// </summary>
		public DataSet ValuesDataSet
		{
			get
			{
				if (this._validValues != null && this._validValues.DataSetReference != null && this._validValues.DataSetReference.Target != null)
				{
					return this._validValues.DataSetReference.Target;
				}
				else
				{
					return null;
				}
			}
		}
		
		/// <summary>
		/// A shortcut to the DataSet used by the DefaultValues object to generate the parameter's list of default values
		/// </summary>
		public DataSet DefaultDataSet
		{
			get
			{
				if (this._defaultValue != null && this._defaultValue.ValidValues != null && this._defaultValue.ValidValues.DataSetReference != null && this._defaultValue.ValidValues.DataSetReference.Target != null)
				{
					return this._defaultValue.ValidValues.DataSetReference.Target;
				}
				else
				{
					return null;
				}
			}
		}

		public bool HasDataSet
		{
			get { return (this.ValuesDataSet != null || this.DefaultDataSet != null); }
		}

		#endregion

		#region CONSTRUCTOR(S)

		/// <summary>
		/// Private constructor used by a Parameter to create deep copies of itself
		/// </summary>
		/// <param name="p">The source Parameter on which to base the new copy</param>
		private Parameter(Parameter p)
		{
			if (p == null)
			{
				throw new ArgumentNullException();
			}
			else
			{
				this._seed = (ReportParameterType)p.CreateSeedCopy();
				this._dsLibrary = p._dsLibrary;
				base.CopyListeners(p);
				this.Initialize();
			}
		}
		
		/// <summary>
		/// Creates a new Parameter instance using objects deserialized from the RDL XML
		/// </summary>
		/// <param name="parm">ReportParameterType is a deserialized .Net representation of the &gt;ReportParameter&lt; XML node with an RDL</param>
		/// <param name="datasets">A list of all the DataSet objects defined within the RDL</param>
		public Parameter(ReportParameterType rpt, Dictionary<string, DataSet> datasets)
		{
			if (rpt == null)
			{
				throw new ArgumentNullException();
			}
			else
			{
				this._seed = rpt;
				this._dsLibrary = datasets;
				this.Initialize();
			}
		}
		protected override void Initialize()
		{
			this._initializing = true;

			this._name = this._seed.Name;

			for (int i = 0; i < this._seed.ItemsElementName.Length; i++)
			{
				switch (this._seed.ItemsElementName[i])
				{
					case ItemsChoiceType33.AllowBlank:
						this._allowBlank = (bool)this._seed.Items[i];
						break;
					case ItemsChoiceType33.DataType:
						this._dataType = (ReportParameterTypeDataType)this._seed.Items[i];
						break;
					case ItemsChoiceType33.DefaultValue:
						DefaultValueType dvt = (DefaultValueType)this._seed.Items[i];
						if (dvt != null && dvt.Items.Length > 0)
						{
							this._defaultValue = new DefaultValues(dvt, this._dsLibrary);
							this._defaultValue.PropertyChanged += this.PropertyChangedListener;
						}
						break;
					case ItemsChoiceType33.Hidden:
						this._hidden = (bool)this._seed.Items[i];
						break;
					case ItemsChoiceType33.MultiValue:
						this._multiValue = (bool)this._seed.Items[i];
						break;
					case ItemsChoiceType33.Nullable:
						this._nullable = (bool)this._seed.Items[i];
						break;
					case ItemsChoiceType33.Prompt:
						this._prompt = this._seed.Items[i].ToString();
						break;
					case ItemsChoiceType33.ValidValues:
						ValidValuesType vvt = (ValidValuesType)this._seed.Items[i];
						if (vvt != null && vvt.Items.Length > 0)
						{
							this._validValues = new ValidValues(vvt.Items, this._dsLibrary, ValueType.ValidValues);
							this._validValues.PropertyChanged += this.PropertyChangedListener;
						}
						break;
					case ItemsChoiceType33.UsedInQuery:
						this._usedInQuery = (bool)this._seed.Items[i];
						break;
				}
			}

			if (this._validValues == null)
			{
				this.CreateValidValues();
			}
			if (this._defaultValue == null || !this._defaultValue.HasData)
			{
				this.CreateDefaultValues();
			}
			this._initializing = true;
		}
		#endregion

		#region PUBLIC METHODS

		#region	..... MISC

		/// <summary>
		/// Determines if a parameter references the indicated DataSet
		/// </summary>
		/// <param name="name">Name of the DataSet to search for</param>
		/// <returns>True if found</returns>
		public bool ReferencesDataSet(string name)
		{
			DataSet dds = this.DefaultDataSet;
			DataSet vds = this.ValuesDataSet;
			if (dds == null && vds == null)
			{
				return false;
			}
			else if ((dds != null && !dds.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase)) || (vds != null && !vds.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase)))
			{
				return true;
			}
			return false;
		}

		/// <summary>
		/// Determines if a parameter references a DataSet that uses the specified stored procedure name
		/// </summary>
		/// <param name="name">Name of the stored procedure to search for</param>
		/// <returns>True if found</returns>
		public bool ReferencesSproc(string name)
		{
			DataSet dds = this.DefaultDataSet;
			DataSet vds = this.ValuesDataSet;

			if (dds == null && vds == null)
			{
				return false;
			}
			else if ((dds != null && dds.Query != null && dds.Query.CommandText.Equals(name, StringComparison.CurrentCultureIgnoreCase)) || (vds != null && vds.Query != null && vds.Query.CommandText.Equals(name, StringComparison.CurrentCultureIgnoreCase)))
			{
				return true;
			}
			return false;
		}

		/// <summary>
		/// Gets the underlying seed object which was originally deserialized from the RDL XML and which is used to
		/// ensure that changes made to the DataSetReference object are written back to the original file when saved.
		/// </summary>
		/// <returns>ReportParameterType</returns>
		public ReportParameterType GetSeedObject()
		{
			return this._seed;
		}

		public void ResetDataSetLibrary(Dictionary<string, DataSet> list)
		{
			if (this._dsLibrary != list)
			{
				this._dsLibrary = list;
				if (this._validValues != null)
				{
					this._validValues.ResetDataSetLibrary(list);
				}
				if (this._defaultValue != null)
				{
					this._defaultValue.ResetDataSetLibrary(list);
				}
			}
		}
		
		#endregion

		#region ..... VALID VALUES
		/// <summary>
		/// Creates a default ValidValues object containing an empty list of ParameterValue objects
		/// </summary>
		public void CreateValidValues()
		{
			this.CreateValidValues(new List<ParameterValue>());
		}

		/// <summary>
		/// Creates a new list-based ValidValues object using the provided collection
		/// </summary>
		/// <param name="list">The list of ParamterValue objects from which to generate the new list-based ValidValues object</param>
		public void CreateValidValues(IList<ParameterValue> list)
		{
			//if not already dirty, save state before making changes
			this.PrepStateChange("ValidValues");

			//create a new ParameterValuesType object which contains the list of items translated as a ParamterValueType[] array
			ParameterValuesType pvts = this.CreateSeed_ParameterValuesType(list);

			//create a new ValidValuesType seed object to wrap the ParameterValuesType seed object
			//(this also ensures that the resulting ValidValuesType object has been added to this._seed.Items[] array)
			ValidValuesType vvt = this.CreateSeed_ValidValuesType(pvts);

			//Create a new ValidValues obect from the ParameterValuesType wrapped seed object, and set the current parameter to it
			this._validValues = new ValidValues(vvt.Items, null, ValueType.ValidValues);

			//Wire-up the PropertyChanged event listener...
			this._validValues.PropertyChanged += this.PropertyChangedListener;
		}
		
		/// <summary>
		/// Creates a new query-based ValidValues object using the provided DataSet
		/// </summary>
		/// <param name="ds">The DataSet that the new ValidValues object will reference</param>
		public void CreateValidValues(DataSet ds)
		{
			//if not already dirty, save state before making changes
			this.PrepStateChange("ValidValues");

			//Wrap the given dataset so we can use it in our existing ValidValues constructor
			Dictionary<string, DataSet> dsd = new Dictionary<string, DataSet>(1);
			dsd.Add(ds.Name, ds);

			//Create a new DataSetReferenceType seed object, which is required to create a query-based ValidValues object
			DataSetReferenceType dsrt = this.CreateSeed_DataSetReferenceType(ds.Name);

			//create a new ValidValuesType seed object to wrap the DataSetReferenceType seed object
			//(this also ensures that the resulting ValidValuesType object has been added to this._seed.Items[] array)
			ValidValuesType vvt = this.CreateSeed_ValidValuesType(dsrt);

			//Create a new ValidValues obect from the DataSetReferenceType wrapped seed object, and set the current property to it
			this._validValues = new ValidValues(vvt.Items, dsd, ValueType.ValidValues);

			//Wire-up the PropertyChanged event listener...
			this._validValues.PropertyChanged += this.PropertyChangedListener;
		}

		/// <summary>
		/// Creates a new ValidValues object based on an existing one. If the existing object does not contain
		/// a valid DataSetReference or ValuesList, then it calls the default CreateValidValues() method instead.
		/// </summary>
		/// <param name="vv">The ValidValues object on which to base the new one</param>
		private void CreateValidValues(ValidValues vv)
		{
			if (vv.DataSetReference != null && vv.DataSetReference.Target != null)
			{
				this.CreateValidValues(vv.DataSetReference.Target);
			}
			else if (vv.ValuesList != null)
			{
				this.CreateValidValues(vv.ValuesList);
			}
			else
			{
				this.CreateValidValues();
			}
		}

		private void DeleteValidValues()
		{
			List<ItemsChoiceType33> elements = new List<ItemsChoiceType33>(this._seed.ItemsElementName);
			int idx = elements.IndexOf(ItemsChoiceType33.ValidValues);

			if (idx > -1)
			{
				this.RemoveSeedItem(elements, idx);
				this._validValues = null;
			}
		}

		#endregion

		#region ..... DEFAULT VALUES

		/// <summary>
		/// Creates a default DefaultValues object containing an empty list of DefaultValue objects
		/// </summary>
		public void CreateDefaultValues()
		{
			this.CreateDefaultValues(new List<DefaultValue>());
		}

		/// <summary>
		/// Creates a new list-based DefaultValues object using the provided collection
		/// </summary>
		/// <param name="list">The list of DefaultValue objects from which to generate the new list-based DefaultValues object</param>
		public void CreateDefaultValues(IList<DefaultValue> dvList)
		{
			//if not already dirty, save state before making changes
			this.PrepStateChange("DefaultValue");

			//create a new ValuesType object which contains the list of values translated as a string[] array
			ValuesType vts = this.CreateSeed_ValuesType(dvList);

			//create a new DefaultValueType object using the new ValuesType seed object, which contains the list of values
			DefaultValueType dvt = this.CreateSeed_DefaultValueType(vts);

			//create a new DefaultValues object from the resultant DefaultValueType seed object
			this._defaultValue = new DefaultValues(dvt, null);
		}
		
		public void CreateDefaultValues(DataSet ds)
		{
			//if not already dirty, save state before making changes
			this.PrepStateChange("DefaultValue");

			//Create a wrapper around the given dataset so we can use it in our constructors
			Dictionary<string, DataSet> dsd = new Dictionary<string, DataSet>(1);
			dsd.Add(ds.Name, ds);

			//Create a new DataSetReferenceType seed object, which is required to create a query-based DefaultValues object
			DataSetReferenceType dsrt = this.CreateSeed_DataSetReferenceType(ds.Name);

			//create a new DefaultValueType seed object to wrap the DataSetReferenceType seed object
			DefaultValueType dvt = this.CreateSeed_DefaultValueType(dsrt);

			//Create a new DefaultValues obect from the DefaultValueType seed object, and set the current property to it
			this._defaultValue = new DefaultValues(dvt, dsd);

			//Wire-up the PropertyChanged event listener...
			this._defaultValue.PropertyChanged += this.PropertyChangedListener;
		}
		
		private void CreateDefaultValues(DefaultValues dv)
		{
			if (dv.ValidValues != null && dv.ValidValues.DataSetReference != null && dv.ValidValues.DataSetReference.Target != null)
			{
				this.CreateDefaultValues(dv.ValidValues.DataSetReference.Target);
			}
			else if (dv.ValidValues.DefaultList != null)
			{
				this.CreateDefaultValues(dv.ValidValues.DefaultList);
			}
			else
			{
				this.CreateDefaultValues();
			}
		}
		private void DeleteDefaultValues()
		{
			List<ItemsChoiceType33> elements = new List<ItemsChoiceType33>(this._seed.ItemsElementName);
			int idx = elements.IndexOf(ItemsChoiceType33.DefaultValue);

			if (idx > -1)
			{
				this.RemoveSeedItem(elements, idx);
				this._defaultValue = null;
			}
		}

		#endregion

		#region ..... INTERFACE IMPLEMENTATION(S)

		/// <summary>
		/// Compares the properties and values of two Parameter objects to determine if they are equivalent
		/// </summary>
		/// <param name="other">The Parameter object to compare the current instance to</param>
		/// <returns>True if the objects are equivalent</returns>
		public override bool EqualTo(Parameter other)
		{
			if (other == null)
			{
				return false;
			}
			if (!CompareTo<ValidValues>.Compare(this.ValidValues, other.ValidValues))
			{
				return false;
			}
			if (!CompareTo<DefaultValues>.Compare(this.DefaultValue, other.DefaultValue))
			{
				return false;
			}
			return
			(
				this.Name == other.Name &&
				this.AllowBlank == other.AllowBlank &&
				this.DataType == other.DataType &&
				this.Hidden == other.Hidden &&
				this.MultiValue == other.MultiValue &&
				this.Nullable == other.Nullable &&
				(other.Prompt == "*" || this.Prompt.Equals(other.Prompt, StringComparison.CurrentCultureIgnoreCase))
			);
		}
		/// <summary>
		/// Copies any changes made in the Parameter object to its underlying seed object, calls the SaveState() method of
		/// any child objects it contains, and resets the IsDirty flag to false.
		/// </summary>
		/// <returns>List of all changes made within the Parameter object and its children</returns>
		public override List<ChangeLog> SaveState()
		{
			List<ChangeLog> log = new List<ChangeLog>();

			if (this._isDirty)
			{
				int idx = -1;

				//get lists of elements originally defined in the seed object
				List<ItemsChoiceType33> elements = null;

				#region ATTRIBUTES
				if (this._name != this._copy._name)
				{
					//name is mandatory
					this._seed.Name = this._name;
					log.Add(new ChangeLog(ChangeType.ParameterName, this._copy._name, this._name));
				}
				if (this._dataType != this._copy._dataType)
				{
					//data type is mandatory...
					elements = new List<ItemsChoiceType33>(this._seed.ItemsElementName);
					idx = elements.IndexOf(ItemsChoiceType33.DataType);
					
					if (idx > -1)
					{
						this._seed.Items[idx] = this._dataType;
						log.Add(new ChangeLog(ChangeType.ParameterDataType, this._copy._dataType, this._dataType));
					}
					else
					{
						this.AddSeedItem(elements, ItemsChoiceType33.DataType, ReportParameterTypeDataType.String);
					}
				}
				if (this._prompt != this._copy._prompt)
				{
					elements = new List<ItemsChoiceType33>(this._seed.ItemsElementName);
					idx = elements.IndexOf(ItemsChoiceType33.Prompt);

					if (string.IsNullOrEmpty(this._prompt)) //if empty or null...
					{
						if (idx > -1) //and a prompt element exists, then remove it from the underlying seed object...
						{
							this.RemoveSeedItem(elements, idx);
						}
					}
					else
					{
						if (idx > -1) //if prompt element already exists, then simply overwrite with the new value
						{
							this._seed.Items[idx] = this._prompt;
						}
						else //add a new element to the underlying seed object...
						{
							this.AddSeedItem(elements, ItemsChoiceType33.Prompt, this._prompt);
						}
					}
					log.Add(new ChangeLog(ChangeType.ParameterPrompt, this._copy._prompt, this._prompt));
				}
				if (this._hidden != this._copy._hidden)
				{
					elements = new List<ItemsChoiceType33>(this._seed.ItemsElementName);
					idx = elements.IndexOf(ItemsChoiceType33.Hidden);

					if (!this._hidden && idx > -1)
					{
						this.RemoveSeedItem(elements, idx);
					}
					else if (this._hidden && idx == -1)
					{
						this.AddSeedItem(elements, ItemsChoiceType33.Hidden, this._hidden);
					}
					else if (this._hidden && idx > -1)
					{
						this._seed.Items[idx] = this._hidden;
					}

					log.Add(new ChangeLog(ChangeType.ParameterHidden, this._copy._hidden, this._hidden));
				}
				if (this._multiValue != this._copy._multiValue)
				{
					elements = new List<ItemsChoiceType33>(this._seed.ItemsElementName);
					idx = elements.IndexOf(ItemsChoiceType33.MultiValue);

					if (!this._multiValue && idx > -1)
					{
						this.RemoveSeedItem(elements, idx);
					}
					else if (this._multiValue && idx == -1)
					{
						this.AddSeedItem(elements, ItemsChoiceType33.MultiValue, this._multiValue);
					}
					else if (this._multiValue && idx > -1)
					{
						this._seed.Items[idx] = this._multiValue;
					}

					log.Add(new ChangeLog(ChangeType.ParameterMultiValue, this._copy._multiValue, this._multiValue));
				}
				if (this._allowBlank != this._copy._allowBlank)
				{
					elements = new List<ItemsChoiceType33>(this._seed.ItemsElementName);
					idx = elements.IndexOf(ItemsChoiceType33.AllowBlank);

					if (!this._allowBlank && idx > -1)
					{
						this.RemoveSeedItem(elements, idx);
					}
					else if (this._allowBlank && idx == -1)
					{
						this.AddSeedItem(elements, ItemsChoiceType33.AllowBlank, this._allowBlank);
					}
					else if (this._allowBlank && idx > -1)
					{
						this._seed.Items[idx] = this._allowBlank;
					}

					log.Add(new ChangeLog(ChangeType.ParameterAllowBlank, this._copy._allowBlank, this._allowBlank));
				}
				if (this._nullable != this._copy._nullable)
				{
					elements = new List<ItemsChoiceType33>(this._seed.ItemsElementName);
					idx = elements.IndexOf(ItemsChoiceType33.Nullable);

					if (!this._nullable && idx > -1)
					{
						this.RemoveSeedItem(elements, idx);
					}
					else if (this._nullable && idx == -1)
					{
						this.AddSeedItem(elements, ItemsChoiceType33.Nullable, this._nullable);
					}
					else if (this._nullable && idx > -1)
					{
						this._seed.Items[idx] = this._nullable;
					}

					log.Add(new ChangeLog(ChangeType.ParameterNullable, this._copy._nullable, this._nullable));
				}
				#endregion

				#region VALID VALUES
				if (!CompareTo<ValidValues>.Compare(this._validValues, this._copy.ValidValues))
				{
					ChangeLog sub = null;
					elements = new List<ItemsChoiceType33>(this._seed.ItemsElementName);

					//if the valid values object has been deleted, remove it from the original seed...
					if ((this._validValues == null && this._copy.ValidValues != null) || this._validValues.ValuesList == null || this._validValues.ValuesList.Count == 0)
					{
						idx = elements.IndexOf(ItemsChoiceType33.ValidValues);
						if (idx > -1)
						{
							this.RemoveSeedItem(elements, idx);
						}
						sub = new ChangeLog(ChangeType.ValidValuesDelete, this._copy._validValues, this._validValues);
					}
					else
					{
						ValidValuesType vvt = new ValidValuesType();

						idx = elements.IndexOf(ItemsChoiceType33.ValidValues);
						if (idx == -1)
						{
							sub = new ChangeLog(ChangeType.ValidValuesAdd, this._copy._validValues, this._validValues);
							sub.RelatedChanges.AddRange(this._validValues.SaveState());
							vvt.Items = this._validValues.GetSeedObject();
							this.AddSeedItem(elements, ItemsChoiceType33.ValidValues, vvt);
						}
						else
						{
							sub = new ChangeLog(ChangeType.ValidValuesChange, this._copy._validValues, this._validValues);
							sub.RelatedChanges.AddRange(this._validValues.SaveState());
							vvt.Items = this._validValues.GetSeedObject();
							this._seed.Items[idx] = vvt;
						}
					}
					log.Add(sub);
				}
				#endregion

				#region DEFAULT VALUES
				if (!CompareTo<DefaultValues>.Compare(this._defaultValue, this._copy.DefaultValue))
				{
					ChangeLog sub = null;

					elements = new List<ItemsChoiceType33>(this._seed.ItemsElementName);

					//if the default values object has been deleted, remove it from the original seed...
					if ((this._defaultValue == null && this._copy.DefaultValue != null) || this._defaultValue.ValidValues == null || this._defaultValue.ValidValues.DefaultList == null || this._defaultValue.ValidValues.DefaultList.Count == 0 )
					{
						//find the index of the DefaultValues object in the items collection
						idx = elements.IndexOf(ItemsChoiceType33.DefaultValue);

						//if it exists, remove it...
						if (idx > -1)
						{
							this.RemoveSeedItem(elements, idx);
						}

						sub = new ChangeLog(ChangeType.DefaultValuesDelete, this._copy.DefaultValue, this._defaultValue);
					}
					else
					{
						idx = elements.IndexOf(ItemsChoiceType33.DefaultValue);

						if (idx == -1)
						{
							//the current DefaultValues object is new
							sub = new ChangeLog(ChangeType.DefaultValuesAdd, this._copy._defaultValue, this._defaultValue);
							sub.RelatedChanges.AddRange(this._defaultValue.SaveState());

							this.AddSeedItem(elements, ItemsChoiceType33.DefaultValue, this._defaultValue.GetSeedObject());
						}
						else
						{
							sub = new ChangeLog(ChangeType.DefaultValuesChange, this._copy._defaultValue, this._defaultValue);
							sub.RelatedChanges.AddRange(this._defaultValue.SaveState());
							this._seed.Items[idx] = this._defaultValue.GetSeedObject();
						}
					}
					log.Add(sub);
				}
				#endregion
			}

			this.ClearState();

			return log;
		}
		
		/// <summary>
		/// Returns the Parameter object's properties and values to the last known clean state and resets the IsDirty flag to false
		/// </summary>
		public override void RestoreState()
		{
			if (this._copy != null)
			{
				this._name = this._copy._name;
				this._dataType = this._copy._dataType;
				this._prompt = this._copy._prompt;
				this._hidden = this._copy._hidden;
				this._multiValue = this._copy._multiValue;
				this._allowBlank = this._copy._allowBlank;
				this._nullable = this._copy._nullable;
				this._validValues = this._copy._validValues;
				this._defaultValue = this._copy._defaultValue;
				this._seed = this._copy._seed;
				this.CopyListeners(this._copy);
			}
			this.ClearState();
		}
		
		/// <summary>
		/// Creates a true copy of the Parameter object and its children
		/// </summary>
		/// <returns>A new Parameter object copy</returns>
		public override Parameter CreateDeepCopy()
		{
			if (this._copy != null)
			{
				return this._copy;
			}
			return new Parameter(this);
		}

		public override object CreateSeedCopy()
		{
			ReportParameterType rpt = new ReportParameterType();
			rpt.Name = this._seed.Name;

			List<ItemsChoiceType33> elements = new List<ItemsChoiceType33>(this._seed.ItemsElementName);
			List<object> items = new List<object>();

			for (int i = 0; i < this._seed.ItemsElementName.Length; i++)
			{
				switch (this._seed.ItemsElementName[i])
				{
					case ItemsChoiceType33.DefaultValue:
						if (this._defaultValue != null)
						{
							items.Add(this._defaultValue.CreateSeedCopy());
						}
						break;

					case ItemsChoiceType33.ValidValues:
						if (this._validValues != null)
						{
							items.Add(this._validValues.CreateSeedCopy());
						}
						break;

					default:
						items.Add(this._seed.Items[i]);
						break;
				}
			}
			rpt.ItemsElementName = elements.ToArray();
			rpt.Items = items.ToArray();

			return rpt;
		}

		#endregion

		#endregion

		#region PRIVATE METHODS

		/// <summary>
		/// Method used internally by the Parameter object to handle the creation of a new ValidValuesType object from the provided object,
		/// which must be either a DataSetReferenceType or and its addition to the underlying seed objects Items[] collection...
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		private ValidValuesType CreateSeed_ValidValuesType(object item)
		{
			if (item is DataSetReferenceType || item is ParameterValuesType)
			{
				//Create a new ValidValuesType seed object from the given object, which can be either a DataSetReferenceType or ParameterValuesType
				ValidValuesType vvt = new ValidValuesType();
				vvt.Items = new object[] { item };

				List<ItemsChoiceType33> parmElements = new List<ItemsChoiceType33>(this._seed.ItemsElementName);

				//if new
				int idx = parmElements.IndexOf(ItemsChoiceType33.ValidValues);
				if (idx == -1)
				{
					//then add the new ValidValuesType seed object and its enum to this._seed.Items[] and this._seed.ItemsElementName[]
					this.AddSeedItem(parmElements, ItemsChoiceType33.ValidValues, vvt);
				}
				else
				{
					this._seed.Items[idx] = vvt;
				}

				return vvt;
			}
			else
			{
				throw new InvalidCastException("Object is not the correct type!");
			}
		}
		
		/// <summary>
		/// Method used internally by the Parameter object to handle the creation of a new DefaultValueType seed object from the provided object, which must be either a
		/// DataSetReferenceType or ValuesType object. It theneither appends it to the underlying ReportParameterType.Items[] array or replaces the existing element with the new creation
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		private DefaultValueType CreateSeed_DefaultValueType(object item)
		{
			//DefaultValueType encapsulates an object[] array, which should contain either a ValuesType or DataSetReference object
			DefaultValueType dvt = new DefaultValueType();
			dvt.Items = new object[] { item };

			//verify if a DefaultValueType element already exists in the root seed object (ReportParameterType)
			List<ItemsChoiceType33> parmElements = new List<ItemsChoiceType33>(this._seed.ItemsElementName);
			int idx = parmElements.IndexOf(ItemsChoiceType33.DefaultValue);

			//if not, we need to redefine the array to include it...
			if (idx == -1)
			{
				//add a DefaultValue enum to this._seed.ItemsElementName[]
				parmElements.Add(ItemsChoiceType33.DefaultValue);
				this._seed.ItemsElementName = parmElements.ToArray();

				//add the new DefaultValueType seed object this._seed.Items[]
				List<object> parmItems = new List<object>(this._seed.Items);
				parmItems.Add(dvt);
				this._seed.Items = parmItems.ToArray();
			}
			else
			{
				//else, replace the existing DefaultValueType seed object with the new one.
				this._seed.Items[idx] = dvt;
			}

			//return the new DefaultValueType seed object
			return dvt;
		}

		private ParameterValuesType CreateSeed_ParameterValuesType(IList<ParameterValue> list)
		{
			//ParameterValuesType encapsulates a ParameterValueType[] array,
			//which defines the list of labels / values, according to the RDL Schema
			ParameterValuesType pvts = new ParameterValuesType();
			List<ParameterValueType> pvtList = new List<ParameterValueType>();

			//each ParameterValueType encapsulates an obect[] array containing a Label and a Value (string)
			//and an ItemsChoiceType32[] array containing the corresponding enums
			List<ItemsChoiceType32> pvtElements = null;
			List<string> pvtItems = null;

			//create an array matching the provided list which will be attached to the underlying ParameterValuesType seed object
			ParameterValueType pvt = null;
			foreach (ParameterValue pv in list)
			{
				pvtElements = new List<ItemsChoiceType32>();
				pvtElements.Add(ItemsChoiceType32.Label);
				pvtElements.Add(ItemsChoiceType32.Value);

				pvtItems = new List<string>();
				pvtItems.Add(pv.Label);
				pvtItems.Add(pv.Value);

				pvt = new ParameterValueType();
				pvt.ItemsElementName = pvtElements.ToArray();
				pvt.Items = pvtItems.ToArray();

				pvtList.Add(pvt);
			}

			//convert the list to a ParameterValueType[] array and return the the parent ParameterValuesType
			pvts.ParameterValue = pvtList.ToArray();
			return pvts;
		}

		private ValuesType CreateSeed_ValuesType(IList<DefaultValue> list)
		{
			//ValuesType encapsulates a string[] array, which defines a list of default values, according to the RDL Schema
			ValuesType vts = new ValuesType();
			List<string> items = new List<string>();

			//create a new list of strings from the provided list, which will be attached to the underlying ValuesType seed object
			foreach (DefaultValue dv in list)
			{
				items.Add(dv.Value);
			}
			if (items.Count == 0)
			{
				items.Add(string.Empty);
			}

			//convert the list to a string[] array and return the the parent ValuesType seed object
			vts.Value = items.ToArray();
			return vts;
		}

		private DataSetReferenceType CreateSeed_DataSetReferenceType(string dsName)
		{
			//DataSetReferenceType encapsulates an array of objects[] that can contain the referenced DataSet's name, and
			//which of its returned Fields (if any) this particular reference uses to generate its list of labels and values
			DataSetReferenceType dsrt = new DataSetReferenceType();

			//create an array of ItemsChoiceType31 enums to map each value in the Items[] array to its appropriate purpose / type
			List<ItemsChoiceType31> dsrtElements = new List<ItemsChoiceType31>();
			dsrtElements.Add(ItemsChoiceType31.DataSetName);
			dsrtElements.Add(ItemsChoiceType31.LabelField);
			dsrtElements.Add(ItemsChoiceType31.ValueField);
			dsrt.ItemsElementName = dsrtElements.ToArray();

			//since this is a new reference, we cannot know before-hand which fields the user will select, so leave them blank for now
			List<object> dsrtItems = new List<object>();
			dsrtItems.Add(dsName);
			dsrtItems.Add(string.Empty);
			dsrtItems.Add(string.Empty);
			dsrt.Items = dsrtItems.ToArray();

			//return the new DataSetReferenceType seed object
			return dsrt;
		}

		private void RemoveSeedItem(List<ItemsChoiceType33> elements, int idx)
		{
			if (idx > -1 && idx < elements.Count)
			{
				elements.RemoveAt(idx);

				List<object> items = new List<object>(this._seed.Items);
				items.RemoveAt(idx);

				this._seed.Items = items.ToArray();
				this._seed.ItemsElementName = elements.ToArray();
			}
		}

		private void AddSeedItem(List<ItemsChoiceType33> elements, ItemsChoiceType33 element, object value)
		{
			if (!elements.Contains(element))
			{
				elements.Add(element);

				List<object> items = new List<object>(this._seed.Items);
				items.Add(value);

				this._seed.ItemsElementName = elements.ToArray();
				this._seed.Items = items.ToArray();
			}
		}

		#endregion
	}
}